<template>
  <div class="flow-container">
    <!-- 左侧操作面板 -->
    <div class="left-panel">
      <el-card shadow="never" header="操作面板">
        <!-- 拖拽面板 -->
        <div class="drag-panel">
          <div class="node-types">
            <div
              v-for="nodeType in nodeTypes"
              :key="nodeType.type"
              class="node-type-item"
              draggable="true"
              @dragstart="onDragStart($event, nodeType.type.toString())"
            >
              <span
                class="color-dot"
                :style="{ backgroundColor: nodeType.color }"
              ></span>
              {{ nodeType.label }}
            </div>
          </div>
        </div>
      </el-card>
    </div>

    <!-- 中间流程图画布 -->
    <div class="center-panel">
      <div
        ref="canvasRef"
        class="flow-canvas"
        @drop="onDrop"
        @dragover="onDragOver"
      >
        <!-- SVG 边渲染 -->
        <svg class="edges" xmlns="http://www.w3.org/2000/svg">
          <g>
            <!-- 使用path绘制直角连接线 -->
            <path
              v-for="edge in edges"
              :key="edge.id"
              :d="getRightAnglePath(edge)"
              fill="none"
              stroke="#999"
              stroke-width="2"
              stroke-dasharray="5,3"
              stroke-linecap="round"
              stroke-linejoin="round"
            />
            <!-- 拖拽连线时的临时线条 -->
            <path
              v-if="isDraggingConnection && linkStartConnector"
              :d="getTempRightAnglePath()"
              fill="none"
              stroke="#999"
              stroke-width="2"
              stroke-dasharray="5,3"
              stroke-linecap="round"
              stroke-linejoin="round"
            />

            <!-- 条件标签 -->
            <text
              v-for="edge in edgesWithConditions"
              :key="`label-${edge.id}`"
              :x="getEdgeLabelPosition(edge).x"
              :y="getEdgeLabelPosition(edge).y"
              class="edge-label"
              text-anchor="middle"
              dominant-baseline="middle"
            >
              {{ edge.condition }}
            </text>
          </g>
        </svg>

        <!-- 连接线删除按钮 -->
        <template v-if="!readonly">
          <div
            v-for="edge in validEdges"
            :key="`delete-${edge.id}`"
            class="edge-delete-button"
            :style="{
              left: getEdgeMidpoint(edge).x - 8 + 'px',
              top: getEdgeMidpoint(edge).y - 8 + 'px',
            }"
            @click.stop="deleteEdge(edge)"
            @mouseenter="hoveredEdge = edge.id"
            @mouseleave="hoveredEdge = null"
          >
            ×
          </div>
        </template>

        <!-- 节点渲染 -->
        <div
          v-for="node in nodes"
          :key="node.id"
          class="node"
          :class="{
            active: selectedNode && selectedNode.id === node.id,
            linking:
              linkStartConnector && linkStartConnector.node_id === node.id,
            'decision-node': isDecisionNode(node.type),
          }"
          :style="{
            left: node.position.x + 'px',
            top: node.position.y + 'px',
            borderColor: getColor(node.type),
          }"
          @mousedown.stop="onNodeMouseDown($event, node)"
          @dragstart.prevent
          @click.stop="onNodeClick(node)"
        >
          <!-- 删除按钮 -->
          <div
            v-if="!readonly"
            class="delete-button"
            @click.stop="deleteNode(node)"
          >
            ×
          </div>

          <!-- 上方连接点 -->
          <div v-if="!readonly" class="connector-top">
            <div
              class="connector-point"
              @mousedown.stop="onConnectorMouseDown($event, node, 'top')"
              @mouseup.stop="onConnectorMouseUp($event, node, 'top')"
            ></div>
          </div>

          <!-- 判断节点的左右连接点 -->
          <template v-if="isDecisionNode(node.type) && !readonly">
            <div class="connector-left">
              <div
                class="connector-point"
                @mousedown.stop="onConnectorMouseDown($event, node, 'left')"
                @mouseup.stop="onConnectorMouseUp($event, node, 'left')"
              ></div>
            </div>
            <div class="connector-right">
              <div
                class="connector-point"
                @mousedown.stop="onConnectorMouseDown($event, node, 'right')"
                @mouseup.stop="onConnectorMouseUp($event, node, 'right')"
              ></div>
            </div>
          </template>

          <div class="node-title">{{ getNodeTypeLabel(node) }}</div>
          <div class="node-content">{{ node.label }}</div>

          <!-- 下方连接点 -->
          <div v-if="!readonly" class="connector-bottom">
            <div
              class="connector-point"
              @mousedown.stop="onConnectorMouseDown($event, node, 'bottom')"
              @mouseup.stop="onConnectorMouseUp($event, node, 'bottom')"
            ></div>
          </div>
        </div>
      </div>
    </div>

    <!-- 右侧属性面板 -->
    <div v-if="selectedNode" class="right-panel">
      <el-card shadow="never" header="节点配置">
        <el-form :model="nodeForm" label-width="80px" size="small">
          <el-form-item label="节点名称">
            <el-input v-model="nodeForm.label" @input="updateNodeLabel" />
          </el-form-item>
          <el-form-item label="节点类型">
            <el-select v-model="nodeForm.type" @change="updateNodeType">
              <el-option
                v-for="nodeType in nodeTypes"
                :key="nodeType.type"
                :label="nodeType.label"
                :value="nodeType.type"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="描述">
            <el-input
              v-model="nodeForm.description"
              type="textarea"
              :rows="3"
              @input="updateNodeDescription"
            />
          </el-form-item>
          <el-form-item label="自定义配置">
            <el-input
              v-model="nodeForm.configJson"
              type="textarea"
              :rows="5"
              placeholder="请输入JSON格式的配置"
              @input="updateNodeConfig"
            />
          </el-form-item>
        </el-form>
      </el-card>
    </div>
  </div>
</template>
  
  <script lang="ts" setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'

// 类型定义
export interface Position {
  x: number
  y: number
}

export type ConnectorPosition = 'top' | 'bottom' | 'left' | 'right'

export interface Connector {
  node_id: string
  position: ConnectorPosition
}

export interface UINode {
  id: string
  position: Position
  type: number
  label: string
  description?: string
  config?: Record<string, any>
}

export interface UIEdge {
  id: string
  source: Connector
  target: Connector
  condition?: string // 条件标签
  conditionType?: 'true' | 'false' | 'custom' // 条件类型
}

export interface FlowData {
  nodes: UINode[]
  edges: UIEdge[]
}

export interface NodeType {
  type: number
  label: string
  color: string
}

// Props
interface Props {
  flowData?: FlowData
  readonly?: boolean
  allowDrop?: boolean
  nodeTypes?: NodeType[]
}

const props = withDefaults(defineProps<Props>(), {
  flowData: () => ({ nodes: [], edges: [] }),
  readonly: false,
  allowDrop: true,
  nodeTypes: () => [
    { type: 1, label: '开始节点', color: '#52c41a' },
    { type: 2, label: '操作节点', color: '#1677ff' },
    { type: 3, label: '结束节点', color: '#f5222d' },
    { type: 4, label: '判断节点', color: '#faad14' },
  ],
})

// Emits
const emit = defineEmits<{
  nodeClick: [node: UINode]
  nodeSelect: [node: UINode | null]
  flowChange: [flowData: FlowData]
  edgeDelete: [edge: UIEdge]
  nodeDelete: [node: UINode]
}>()

// 画布 DOM
const canvasRef = ref<HTMLElement | null>(null)

// 画布数据
const nodes = ref<UINode[]>([])
const edges = ref<UIEdge[]>([])
const selectedNode = ref<UINode | null>(null)

// 节点编辑表单
const nodeForm = ref({
  label: '',
  type: 1,
  description: '',
  configJson: '',
})

// 拖拽移动状态
const draggingId = ref<string | null>(null)
const dragOffset = ref<Position>({ x: 0, y: 0 })
const wasDragging = ref(false)

// 连线状态
const linkStartConnector = ref<Connector | null>(null)
const isDraggingConnection = ref(false)
const dragConnectionEnd = ref<Position>({ x: 0, y: 0 })
const hoveredEdge = ref<string | null>(null)

// 监听props变化，更新内部数据
watch(
  () => props.flowData,
  (newData) => {
    if (newData && newData.nodes && newData.edges) {
      nextTick(() => {
        nodes.value = JSON.parse(JSON.stringify(newData.nodes || []))
        edges.value = JSON.parse(JSON.stringify(newData.edges || []))
      })
    }
  },
  { immediate: true, deep: true },
)

// 监听选中节点变化，更新表单
watch(
  selectedNode,
  (node) => {
    if (node) {
      nodeForm.value = {
        label: node.label || '',
        type: node.type || 1,
        description: node.description || '',
        configJson: node.config ? JSON.stringify(node.config, null, 2) : '',
      }
    }
  },
  { immediate: true },
)

// 当内部数据变化时，触发事件
const emitFlowChange = () => {
  emit('flowChange', {
    nodes: JSON.parse(JSON.stringify(nodes.value)),
    edges: JSON.parse(JSON.stringify(edges.value)),
  })
}

// 获取节点类型标签
const getNodeTypeLabel = (node: UINode): string => {
  const nodeType = props.nodeTypes.find((nt) => nt.type === node.type)
  return nodeType ? nodeType.label : node.label
}

// 使用节流函数优化数据变化通知
let updateTimer: ReturnType<typeof setTimeout> | null = null
const notifyDataChange = () => {
  if (updateTimer) {
    clearTimeout(updateTimer)
  }
  updateTimer = setTimeout(() => {
    emitFlowChange()
    updateTimer = null
  }, 16) // ~60fps
}

const getColor = (type: number) => {
  const nodeType = props.nodeTypes.find((nt) => nt.type === type)
  return nodeType ? nodeType.color : '#1677ff'
}

// 优化：计算属性缓存有效连接线
const validEdges = computed(() => {
  return edges.value.filter((edge) => {
    const sourceNode = nodes.value.find((n) => n.id === edge.source.node_id)
    const targetNode = nodes.value.find((n) => n.id === edge.target.node_id)
    return sourceNode && targetNode
  })
})

// 计算属性：带条件的连接线
const edgesWithConditions = computed(() => {
  return validEdges.value.filter((edge) => {
    return edge.condition && isFromDecisionNode(edge)
  })
})

// 判断节点类型
const isDecisionNode = (type: number) => type === 4

// 判断是否来自判断节点
const isFromDecisionNode = (edge: UIEdge): boolean => {
  const sourceNode = nodes.value.find((n) => n.id === edge.source.node_id)
  return sourceNode ? isDecisionNode(sourceNode.type) : false
}

// 获取条件标签位置
const getEdgeLabelPosition = (edge: UIEdge): Position => {
  const startPos = getConnectorPosition(edge.source)
  const endPos = getConnectorPosition(edge.target)

  // 在连线的1/3处放置标签
  return {
    x: startPos.x + (endPos.x - startPos.x) * 0.3,
    y: startPos.y + (endPos.y - startPos.y) * 0.3,
  }
}

// 获取节点的连接数
const getNodeConnections = (nodeId: string) => {
  const inputs = edges.value.filter((edge) => edge.target.node_id === nodeId)
  const outputs = edges.value.filter((edge) => edge.source.node_id === nodeId)
  return { inputs, outputs }
}

// 节点编辑相关方法
const updateNodeLabel = () => {
  if (selectedNode.value) {
    selectedNode.value.label = nodeForm.value.label
    notifyDataChange()
  }
}

const updateNodeType = () => {
  if (selectedNode.value) {
    selectedNode.value.type = nodeForm.value.type
    notifyDataChange()
  }
}

const updateNodeDescription = () => {
  if (selectedNode.value) {
    selectedNode.value.description = nodeForm.value.description
    notifyDataChange()
  }
}

const updateNodeConfig = () => {
  if (selectedNode.value) {
    try {
      if (nodeForm.value.configJson.trim()) {
        selectedNode.value.config = JSON.parse(nodeForm.value.configJson)
      } else {
        selectedNode.value.config = {}
      }
      notifyDataChange()
    } catch (error) {
      ElMessage.warning('配置JSON格式错误')
    }
  }
}

// 拖拽开始
const onDragStart = (event: DragEvent, type: string) => {
  event.dataTransfer?.setData('application/mango-flow', type)
  event.dataTransfer!.effectAllowed = 'move'
}

// 允许放置
const onDragOver = (event: DragEvent) => {
  if (!props.allowDrop || props.readonly) return
  event.preventDefault()
  event.dataTransfer!.dropEffect = 'move'
}

// 计算鼠标在画布内坐标
const getCanvasPosition = (clientX: number, clientY: number): Position => {
  const el = canvasRef.value!
  const rect = el.getBoundingClientRect()
  return { x: clientX - rect.left, y: clientY - rect.top }
}

// 放置到画布：创建节点
const onDrop = (event: DragEvent) => {
  if (!props.allowDrop || props.readonly) return
  event.preventDefault()
  const type = event.dataTransfer?.getData('application/mango-flow')
  if (!type) return

  const position = getCanvasPosition(event.clientX, event.clientY)
  const id = `${type}-${Date.now()}`
  const nodeType = parseInt(type, 10)
  const nodeTypeInfo = props.nodeTypes.find((nt) => nt.type === nodeType)

  const newNode: UINode = {
    id,
    position,
    type: nodeType,
    label: nodeTypeInfo ? nodeTypeInfo.label : `${type} 节点`,
    description: '',
    config: {},
  }
  nodes.value = [...nodes.value, newNode]
  notifyDataChange()
}

// 节点点击事件
const onNodeClick = (node: UINode) => {
  if (!wasDragging.value) {
    selectedNode.value = node
    emit('nodeClick', node)
    emit('nodeSelect', node)
  }
}

// 节点鼠标按下：开始拖拽
const onNodeMouseDown = (e: MouseEvent, node: UINode) => {
  if (props.readonly || e.button !== 0) return
  e.preventDefault()
  const start = getCanvasPosition(e.clientX, e.clientY)
  draggingId.value = node.id
  dragOffset.value = {
    x: start.x - node.position.x,
    y: start.y - node.position.y,
  }
  wasDragging.value = false
}

// 节流优化的鼠标移动事件
let mouseMoveTimer: number | null = null
const throttledMouseMove = (e: MouseEvent) => {
  if (mouseMoveTimer) return

  mouseMoveTimer = requestAnimationFrame(() => {
    onMouseMove(e)
    mouseMoveTimer = null
  })
}

// 鼠标移动：更新拖拽节点坐标和连线位置
const onMouseMove = (e: MouseEvent) => {
  if (props.readonly) return

  if (draggingId.value) {
    // 节点拖拽逻辑
    const pos = getCanvasPosition(e.clientX, e.clientY)
    const idx = nodes.value.findIndex((n) => n.id === draggingId.value)
    if (idx !== -1) {
      const currentNode = nodes.value[idx]
      const newX = pos.x - dragOffset.value.x
      const newY = pos.y - dragOffset.value.y

      // 只有位置真正改变时才更新
      if (
        Math.abs(newX - currentNode.position.x) > 1 ||
        Math.abs(newY - currentNode.position.y) > 1
      ) {
        wasDragging.value = true
        currentNode.position.x = newX
        currentNode.position.y = newY

        // 清理连接点位置缓存
        connectorPositionCache.delete(`${currentNode.id}-top`)
        connectorPositionCache.delete(`${currentNode.id}-bottom`)

        // 通知数据变化
        notifyDataChange()
      }
    }
  }

  if (isDraggingConnection.value) {
    // 连线拖拽逻辑
    const pos = getCanvasPosition(e.clientX, e.clientY)
    dragConnectionEnd.value = pos
  }
}

// 鼠标抬起：结束拖拽
const onMouseUp = () => {
  // 重置拖拽状态
  if (draggingId.value) {
    setTimeout(() => {
      wasDragging.value = false
    }, 10)
  }

  draggingId.value = null
  dragOffset.value = { x: 0, y: 0 }

  // 结束连线拖拽
  if (isDraggingConnection.value) {
    isDraggingConnection.value = false
    linkStartConnector.value = null
  }
}

onMounted(() => {
  window.addEventListener('mousemove', throttledMouseMove, { passive: true })
  window.addEventListener('mouseup', onMouseUp)
})

onBeforeUnmount(() => {
  window.removeEventListener('mousemove', throttledMouseMove)
  window.removeEventListener('mouseup', onMouseUp)
  if (mouseMoveTimer) {
    cancelAnimationFrame(mouseMoveTimer)
  }
  if (updateTimer) {
    clearTimeout(updateTimer)
  }
})

// 连接点鼠标按下：开始拖拽连线
const onConnectorMouseDown = (
  e: MouseEvent,
  node: UINode,
  position: ConnectorPosition,
) => {
  if (props.readonly) return
  e.stopPropagation()
  e.preventDefault()

  const currentConnector: Connector = { node_id: node.id, position }
  linkStartConnector.value = currentConnector
  isDraggingConnection.value = true

  // 获取初始位置
  const startPos = getConnectorPosition(currentConnector)
  dragConnectionEnd.value = { x: startPos.x, y: startPos.y }
}

// 连接点鼠标抬起：处理连线
const onConnectorMouseUp = (
  e: MouseEvent,
  node: UINode,
  position: ConnectorPosition,
) => {
  if (props.readonly) return
  e.stopPropagation()

  draggingId.value = null
  dragOffset.value = { x: 0, y: 0 }

  // 如果在拖拽连线状态，尝试创建连接
  if (isDraggingConnection.value && linkStartConnector.value) {
    const currentConnector: Connector = { node_id: node.id, position }

    // 不能连接到自己
    if (
      linkStartConnector.value.node_id !== node.id ||
      linkStartConnector.value.position !== position
    ) {
      if (isValidConnection(linkStartConnector.value, currentConnector)) {
        // 创建连线
        createConnection(linkStartConnector.value, currentConnector)
      }
    }

    // 结束连线状态
    isDraggingConnection.value = false
    linkStartConnector.value = null
    return
  }

  if (wasDragging.value) {
    wasDragging.value = false
    return
  }

  const currentConnector: Connector = { node_id: node.id, position }

  if (!linkStartConnector.value) {
    // 设置起点
    linkStartConnector.value = currentConnector
  } else if (
    linkStartConnector.value.node_id === node.id &&
    linkStartConnector.value.position === position
  ) {
    // 点击同一个连接点，取消连线
    linkStartConnector.value = null
  } else {
    if (isValidConnection(linkStartConnector.value, currentConnector)) {
      // 创建连线
      createConnection(linkStartConnector.value, currentConnector)
    }

    linkStartConnector.value = null
  }
}

// 创建连接
const createConnection = (source: Connector, target: Connector) => {
  const sourceNode = nodes.value.find((n) => n.id === source.node_id)

  if (sourceNode && isDecisionNode(sourceNode.type)) {
    // 为判断节点的连接添加条件
    let condition = ''
    let conditionType: 'true' | 'false' | 'custom' = 'custom'

    if (source.position === 'left') {
      condition = 'False'
      conditionType = 'false'
    } else if (source.position === 'right') {
      condition = 'True'
      conditionType = 'true'
    }

    const edge: UIEdge = {
      id: `e-${Date.now()}`,
      source,
      target,
      condition,
      conditionType,
    }

    edges.value = [...edges.value, edge]
  } else {
    // 普通连接
    const edge: UIEdge = {
      id: `e-${Date.now()}`,
      source,
      target,
    }
    edges.value = [...edges.value, edge]
  }

  notifyDataChange()
}

// 验证连接是否有效
const isValidConnection = (source: Connector, target: Connector): boolean => {
  const startNode = nodes.value.find((n) => n.id === source.node_id)
  const endNode = nodes.value.find((n) => n.id === target.node_id)
  if (!startNode || !endNode) return false

  const isStartDecision = isDecisionNode(startNode.type)
  const isEndDecision = isDecisionNode(endNode.type)

  // 获取起点和终点节点的当前连接情况
  const startConnections = getNodeConnections(source.node_id)
  const endConnections = getNodeConnections(target.node_id)

  // 检查连接是否有效
  let isValid = false
  let errorMessage = ''

  if (isStartDecision && isEndDecision) {
    // 判断节点之间不允许连接
    errorMessage = '判断类型节点之间不允许连接'
  } else if (isStartDecision) {
    // 起点是判断节点
    if (source.position === 'left' || source.position === 'right') {
      // 检查该方向是否已有连接
      const existingConnection = startConnections.outputs.find(
        (edge) => edge.source.position === source.position,
      )
      if (existingConnection) {
        errorMessage = '该分支已存在连接'
      } else if (target.position === 'top') {
        isValid = true
      } else {
        errorMessage = '只能连接到目标节点的上方'
      }
    } else {
      errorMessage = '判断节点只能从左右两侧发出连接'
    }
  } else if (isEndDecision) {
    // 起点是普通节点，终点是判断节点
    if (source.position === 'bottom' && startConnections.outputs.length > 0) {
      errorMessage = '普通节点最多只能有一个输出连接'
    } else if (source.position === 'top') {
      errorMessage = '普通节点只能从下方发出连接'
    } else if (target.position === 'top') {
      isValid = true
    } else {
      errorMessage = '只能连接到判断节点的上方'
    }
  } else {
    // 普通节点之间的连接
    if (source.position !== 'bottom' || target.position !== 'top') {
      errorMessage = '普通节点只能从上方接收连接，从下方发出连接'
    } else if (startConnections.outputs.length > 0) {
      errorMessage = '普通节点最多只能有一个输出连接'
    } else if (endConnections.inputs.length > 0) {
      errorMessage = '普通节点最多只能有一个输入连接'
    } else {
      isValid = true
    }
  }

  if (!isValid && errorMessage) {
    ElMessage.warning(`连接无效：${errorMessage}`)
  }

  return isValid
}

// 优化：缓存连接点位置计算
const connectorPositionCache = new Map<string, Position>()

// 用于计算连接点坐标（供 SVG 使用）
const getConnectorPosition = (connector: Connector): Position => {
  const cacheKey = `${connector.node_id}-${connector.position}`

  const node = nodes.value.find((n) => n.id === connector.node_id)
  if (!node) return { x: 0, y: 0 }

  // 判断节点使用圆形尺寸，普通节点使用矩形尺寸
  const isDecision = isDecisionNode(node.type)
  const nodeWidth = isDecision ? 80 : 140
  const nodeHeight = isDecision ? 80 : 56

  // 基础位置（节点左上角）
  const baseX = node.position.x
  const baseY = node.position.y

  // 根据连接点位置计算坐标
  let result: Position
  switch (connector.position) {
    case 'top':
      result = { x: baseX + nodeWidth / 2, y: baseY }
      break
    case 'bottom':
      result = { x: baseX + nodeWidth / 2, y: baseY + nodeHeight }
      break
    case 'left':
      result = { x: baseX, y: baseY + nodeHeight / 2 }
      break
    case 'right':
      result = { x: baseX + nodeWidth, y: baseY + nodeHeight / 2 }
      break
    default:
      result = { x: baseX + nodeWidth / 2, y: baseY + nodeHeight / 2 }
  }

  // 更新缓存
  connectorPositionCache.set(cacheKey, result)
  return result
}

// 计算90度直角连接线的SVG路径
const getRightAnglePath = (edge: UIEdge): string => {
  const startPos = getConnectorPosition(edge.source)
  const endPos = getConnectorPosition(edge.target)

  // 计算两个节点X坐标的中间位置
  const midX = (startPos.x + endPos.x) / 2

  // 从起点垂直下降，然后在中间X位置水平移动，最后垂直连接到终点
  return `M ${startPos.x} ${startPos.y}
            L ${startPos.x} ${startPos.y + 20}
            L ${midX} ${startPos.y + 20}
            L ${midX} ${endPos.y - 20}
            L ${endPos.x} ${endPos.y - 20}
            L ${endPos.x} ${endPos.y}`
}

// 计算拖拽时临时连接线的SVG路径
const getTempRightAnglePath = (): string => {
  if (!linkStartConnector.value) return ''

  const startPos = getConnectorPosition(linkStartConnector.value)
  const endPos = dragConnectionEnd.value

  // 计算两个位置X坐标的中间位置
  const midX = (startPos.x + endPos.x) / 2

  // 使用相同的路径算法
  return `M ${startPos.x} ${startPos.y}
            L ${startPos.x} ${startPos.y + 20}
            L ${midX} ${startPos.y + 20}
            L ${midX} ${endPos.y - 20}
            L ${endPos.x} ${endPos.y - 20}
            L ${endPos.x} ${endPos.y}`
}

// 计算连接线中点坐标（用于放置删除按钮）
const getEdgeMidpoint = (edge: UIEdge): Position => {
  const startPos = getConnectorPosition(edge.source)
  const endPos = getConnectorPosition(edge.target)

  // 在中间的垂直线段上放置删除按钮
  const midX = (startPos.x + endPos.x) / 2
  const midY = startPos.y + (endPos.y - startPos.y) / 2

  return {
    x: midX,
    y: midY,
  }
}

// 删除连接线
const deleteEdge = (edge: UIEdge) => {
  if (props.readonly) return
  edges.value = edges.value.filter((e) => e.id !== edge.id)
  emit('edgeDelete', edge)
  notifyDataChange()
}

// 删除节点及相关连线
const deleteNode = (node: UINode) => {
  if (props.readonly) return

  // 删除与该节点相关的所有连接线
  edges.value = edges.value.filter(
    (edge) =>
      edge.source.node_id !== node.id && edge.target.node_id !== node.id,
  )

  // 如果正在连线且起点是该节点，取消连线状态
  if (
    linkStartConnector.value &&
    linkStartConnector.value.node_id === node.id
  ) {
    linkStartConnector.value = null
  }

  // 如果选中的是该节点，取消选中
  if (selectedNode.value && selectedNode.value.id === node.id) {
    selectedNode.value = null
    emit('nodeSelect', null)
  }

  // 删除节点
  nodes.value = nodes.value.filter((n) => n.id !== node.id)
  emit('nodeDelete', node)
  notifyDataChange()
}

// 暴露方法给父组件
defineExpose({
  clearSelection: () => {
    selectedNode.value = null
    emit('nodeSelect', null)
  },
  selectNode: (nodeId: string) => {
    const node = nodes.value.find((n) => n.id === nodeId)
    if (node) {
      selectedNode.value = node
      emit('nodeSelect', node)
    }
  },
  getFlowData: () => ({
    nodes: JSON.parse(JSON.stringify(nodes.value)),
    edges: JSON.parse(JSON.stringify(edges.value)),
  }),
})
</script>
  
  <style scoped>
.flow-container {
  display: flex;
  height: 100%;
  width: 100%;
}

.left-panel {
  width: 200px;
  height: 100%;
  border-right: 1px solid var(--el-border-color-light);
  background-color: #fff;
}

.right-panel {
  width: 300px;
  height: 100%;
  border-left: 1px solid var(--el-border-color-light);
  background-color: #fff;
  overflow-y: auto;
}

.center-panel {
  flex: 1;
  height: 100%;
}

.drag-panel {
  padding: 8px 0;
}

.node-types {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.node-type-item {
  display: flex;
  align-items: center;
  padding: 8px 12px;
  border: 1px solid var(--el-border-color-light);
  border-radius: 6px;
  cursor: grab;
  transition: all 0.2s;
  background-color: #fff;
}

.node-type-item:hover {
  background-color: var(--el-fill-color-light);
  border-color: var(--el-color-primary);
}

.node-type-item:active {
  cursor: grabbing;
}

.color-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 8px;
  flex-shrink: 0;
}

.flow-canvas {
  position: relative;
  width: 100%;
  height: 100%;
  border: 1px solid var(--el-border-color-light);
  border-radius: 8px;
  overflow: auto;
  background-color: #f5f5f5;
  background-image: linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.08) 1px,
      transparent 0
    ),
    linear-gradient(rgba(0, 0, 0, 0.08) 1px, transparent 0);
  background-size: 20px 20px;
  min-height: 600px;
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* IE and Edge */
}

/* 隐藏WebKit浏览器的滚动条 */
.flow-canvas::-webkit-scrollbar {
  display: none;
}

.edges {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  min-height: 2000px;
  pointer-events: none;
}

.node {
  position: absolute;
  background: #fff;
  padding: 8px 10px;
  border: 2px solid var(--el-color-primary);
  border-radius: 8px;
  min-width: 140px;
  min-height: 56px;
  cursor: grab;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  user-select: none;
}

/* 判断节点圆形样式 */
.decision-node {
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  width: 80px;
  height: 80px;
  min-width: 80px;
  min-height: 80px;
  padding: 4px;
}

.node:active {
  cursor: grabbing;
}

.node.active {
  box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.2);
}

.node.linking {
  outline: 2px dashed var(--el-color-primary);
}

.delete-button {
  position: absolute;
  top: -8px;
  right: -8px;
  width: 16px;
  height: 16px;
  background-color: var(--el-color-danger);
  color: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.2s;
  z-index: 20;
}

.node:hover .delete-button {
  opacity: 1;
}

.edge-delete-button {
  position: absolute;
  width: 16px;
  height: 16px;
  background-color: var(--el-color-danger);
  color: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  cursor: pointer;
  opacity: 0.8;
  transition: all 0.2s;
  z-index: 30;
  border: 2px solid white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.edge-delete-button:hover {
  opacity: 1;
  transform: scale(1.1);
}

.connector-top,
.connector-bottom {
  display: flex;
  justify-content: center;
  width: 100%;
  position: absolute;
  left: 0;
}

.connector-top {
  top: -8px;
}

.connector-bottom {
  bottom: -8px;
}

/* 左右连接点样式 */
.connector-left,
.connector-right {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
}

.connector-left {
  left: -8px;
}

.connector-right {
  right: -8px;
}

.connector-point {
  width: 14px;
  height: 14px;
  background-color: #fff;
  border: 3px solid var(--el-color-primary);
  border-radius: 50%;
  cursor: pointer;
  z-index: 10;
}

.connector-point:hover {
  background-color: var(--el-color-primary);
}

.node-title {
  font-weight: 600;
  margin-bottom: 4px;
  color: #000;
}

.node-content {
  font-size: 12px;
  color: #666;
}

/* 条件标签样式 */
.edge-label {
  font-size: 12px;
  fill: #333;
  font-weight: 500;
  text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.8);
}
</style>